package net.csdn.business.gateway.handler;

import lombok.extern.slf4j.Slf4j;
import net.csdn.business.common.oauth2.model.entity.GatewayRouteInfo;
import net.csdn.business.common.oauth2.service.IGatewayRouteInfoService;
import net.csdn.business.gateway.service.impl.CacheRouteDefinitionRepository;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * GatewayRouteHandler
 * @author zhangyl
 * @since 2022/12/13 14:53
 */
@Slf4j
@Component
public class GatewayRouteHandler implements ApplicationEventPublisherAware, CommandLineRunner {

    private ApplicationEventPublisher publisher;

    /*@Autowired
    private RouteDefinitionWriter routeDefinitionWriter;*/

    @Autowired
    private IGatewayRouteInfoService gatewayRouteInfoService;

    @Autowired
    private CacheRouteDefinitionRepository cacheRouteDefinitionRepository;


    @Override
    public void run(String... args) throws Exception {
        log.info("首次初始化路由....");
        this.loadRouteConfig();
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    public void loadRouteConfig() {
        log.info("加载路由配置...");
        //清空路由缓存信息
        cacheRouteDefinitionRepository.clear();
        // 从数据库查询数据
        List<GatewayRouteInfo> gatewayRoutes = gatewayRouteInfoService.selectGatewayRouteInfoList();
        gatewayRoutes.forEach(gatewayRoute -> {
            RouteDefinition definition = handleData(gatewayRoute);
            // 保存路由
            cacheRouteDefinitionRepository.save(Mono.just(definition)).subscribe();
        });
        // 发布事件，通知更新数据
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }


    /**
     * 保存路由
     * @param gatewayRoute
     */
    public void saveRoute(GatewayRouteInfo gatewayRoute){
        // GatewayRoute转为RouteDefinition
        log.info("-----GatewayRouteHandler addRoute and refresh routeId:{}",gatewayRoute.getRouteId());
        RouteDefinition definition = handleData(gatewayRoute);
        // 保存路由数据
        cacheRouteDefinitionRepository.save(Mono.just(definition)).subscribe();
        // 发布事件，通知更新数据
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    /**
     * 更新路由
     * @param gatewayRoute
     */
    public void updateRoute(GatewayRouteInfo gatewayRoute){
        log.info("-----GatewayRouteHandler updateRoute and refresh routeId:{}",gatewayRoute.getRouteId());
        // GatewayRoute转为RouteDefinition
        RouteDefinition definition = handleData(gatewayRoute);
        // 根据路由ID删除路由信息
        cacheRouteDefinitionRepository.delete(Mono.just(gatewayRoute.getRouteId())).subscribe();
        // 重新保存路由数据
        cacheRouteDefinitionRepository.save(Mono.just(definition)).subscribe();
        // 发布事件，通知更新数据
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    public void deleteRoute(String routeId) {
        log.info("-----GatewayRouteHandler delete and refresh routeId:{}",routeId);
        cacheRouteDefinitionRepository.delete(Mono.just(routeId)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    /**
     * GatewayRoute转RouteDefinition
     * @param gatewayRoute
     * @return
     */
    public RouteDefinition handleData(GatewayRouteInfo gatewayRoute){
        RouteDefinition routeDefinition = new RouteDefinition();

        URI uri = null;
        // 判断Uri是不是http地址
        if (gatewayRoute.getUri().startsWith("http")){
            // http地址
            uri = UriComponentsBuilder.fromHttpUrl(gatewayRoute.getUri()).build().toUri();
        }else {
            // 微服务服务名
            uri = UriComponentsBuilder.fromUriString("lb://"+gatewayRoute.getUri()).build().toUri();
        }
        // 设置路由ID
        routeDefinition.setId(gatewayRoute.getRouteId());
        // 设置uri
        routeDefinition.setUri(uri);
        //谓语(路由转发条件)
        List<PredicateDefinition> predicates=new ArrayList<>();
        PredicateDefinition predicatePath = new PredicateDefinition();
        predicatePath.setName("Path");
        Map<String,String> predicateArgsPath = new HashMap<>();
        predicateArgsPath.put("patterns",gatewayRoute.getReqPath());
        predicatePath.setArgs(predicateArgsPath);
        predicates.add(predicatePath);
        PredicateDefinition predicateMethod = new PredicateDefinition();
        predicateMethod.setName("Method");
        Map<String,String> predicateArgsMethod = new HashMap<>();
        predicateArgsMethod.put("methods",gatewayRoute.getReqMethod());
        predicateMethod.setArgs(predicateArgsMethod);
        predicates.add(predicateMethod);
        routeDefinition.setPredicates(predicates);
        List<FilterDefinition> filters=new ArrayList<>();
       /* FilterDefinition filterAddHeader=new FilterDefinition();
        filterAddHeader.setName("AddRequestHeader");
        Map<String,String> filterAddHeaderArgs = new HashMap<>();
        filterAddHeaderArgs.put("name", GatewayConstants.HEADER_URI_SCOPE);
        filterAddHeaderArgs.put("value",gatewayRoute.getScope());
        filterAddHeader.setArgs(filterAddHeaderArgs);
        filters.add(filterAddHeader);*/
        //配置认证过滤器,解析用户信息，并校验用户选择的scope
        FilterDefinition filterAuthentication=new FilterDefinition();
        filterAuthentication.setName("Authentication");
        Map<String,String> filterAuthenticationArgs = new HashMap<>();
        filterAuthenticationArgs.put("isAuth", gatewayRoute.getIsAuth().toString());
        // filterAuthenticationArgs.put("scope", gatewayRoute.getScope());
        filterAuthentication.setArgs(filterAuthenticationArgs);
        filters.add(filterAuthentication);
        // 添加限速过滤器
        FilterDefinition filterRateLimit = new FilterDefinition();
        filterRateLimit.setName("CustomRateLimit");
        filters.add(filterRateLimit);
        //判断是否配置了url重写表达式，如果配置，则添加RewritePath过滤器
        String rewritePath=gatewayRoute.getRewritePath();
        if(StringUtils.isNotBlank(rewritePath)){
            String []params=rewritePath.split(",");
            if(params.length==2){
                FilterDefinition filterRewritePath=new FilterDefinition();
                filterRewritePath.setName("RewritePath");
                Map<String,String> filterRewritePathArgs = new HashMap<>();
                filterRewritePathArgs.put("regexp", params[0]);
                filterRewritePathArgs.put("replacement", params[1]);
                //filterAuthenticationArgs.put("scope", gatewayRoute.getScope());
                filterRewritePath.setArgs(filterRewritePathArgs);
                filters.add(filterRewritePath);
            }
        }
        routeDefinition.setFilters(filters);
        // 设置ord
        if (null != gatewayRoute.getRouteOrder()) {
            routeDefinition.setOrder(gatewayRoute.getRouteOrder());
        }

        // 增加router meta
        Map<String, Object> metadata = new HashMap<String, Object>(1) {{
            put("pattern", buildPattern(gatewayRoute.getReqPath()));
        }};
        routeDefinition.setMetadata(metadata);
        return routeDefinition;
    }

    private PathPattern buildPattern(String path) {
        PathPatternParser parser = new PathPatternParser();
        return parser.parse(path);
    }
}